﻿//NGit is derived from jgit (http://eclipse.org/jgit).
//Copyright owners are documented in jgit's IP log.

//This program and the accompanying materials are made available
//under the terms of the Eclipse Distribution License v1.0 which
//accompanies this distribution, is reproduced below, and is
//available at http://www.eclipse.org/org/documents/edl-v10.php

//All rights reserved.

//Redistribution and use in source and binary forms, with or
//without modification, are permitted provided that the following
//conditions are met:

//- Redistributions of source code must retain the above copyright
//  notice, this list of conditions and the following disclaimer.

//- Redistributions in binary form must reproduce the above
//  copyright notice, this list of conditions and the following
//  disclaimer in the documentation and/or other materials provided
//  with the distribution.

//- Neither the name of the Eclipse Foundation, Inc. nor the
//  names of its contributors may be used to endorse or promote
//  products derived from this software without specific prior
//  written permission.

//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
//CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
//INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
//SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
//NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
//STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
//ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;



namespace SharpDBM.helper
{
    public class InputStream : IDisposable
    {
        private long mark;
        protected Stream Wrapped;
        protected Stream BaseStream;

        public static implicit operator InputStream(Stream s)
        {
            return Wrap(s);
        }

        public static implicit operator Stream(InputStream s)
        {
            return s.GetWrappedStream();
        }

        public virtual int Available()
        {
            if (Wrapped is WrappedSystemStream)
                return ((WrappedSystemStream)Wrapped).InputStream.Available();
            else
                return 0;
        }

        public virtual void Close()
        {
            if (Wrapped != null)
            {
                Wrapped.Close();
            }
        }

        public void Dispose()
        {
            Close();
        }

        public Stream GetWrappedStream()
        {
            // Always create a wrapper stream (not directly Wrapped) since the subclass
            // may be overriding methods that need to be called when used through the Stream class
            return new WrappedSystemStream(this);
        }

        public virtual void Mark(int readlimit)
        {
            if (Wrapped is WrappedSystemStream)
                ((WrappedSystemStream)Wrapped).InputStream.Mark(readlimit);
            else
            {
                if (BaseStream is WrappedSystemStream)
                    ((WrappedSystemStream)BaseStream).OnMark(readlimit);
                if (Wrapped != null)
                    this.mark = Wrapped.Position;
            }
        }

        public virtual bool MarkSupported()
        {
            if (Wrapped is WrappedSystemStream)
                return ((WrappedSystemStream)Wrapped).InputStream.MarkSupported();
            else
                return ((Wrapped != null) && Wrapped.CanSeek);
        }

        public virtual int Read()
        {
            if (Wrapped == null)
            {
                throw new NotImplementedException();
            }
            return Wrapped.ReadByte();
        }

        public virtual int Read(byte[] buf)
        {
            return Read(buf, 0, buf.Length);
        }

        public virtual int Read(byte[] b, int off, int len)
        {
            if (Wrapped is WrappedSystemStream)
                return ((WrappedSystemStream)Wrapped).InputStream.Read(b, off, len);

            if (Wrapped != null)
            {
                int num = Wrapped.Read(b, off, len);
                return ((num <= 0) ? -1 : num);
            }
            int totalRead = 0;
            while (totalRead < len)
            {
                int nr = Read();
                if (nr == -1)
                    return -1;
                b[off + totalRead] = (byte)nr;
                totalRead++;
            }
            return totalRead;
        }

        public virtual void Reset()
        {
            if (Wrapped is WrappedSystemStream)
                ((WrappedSystemStream)Wrapped).InputStream.Reset();
            else
            {
                if (Wrapped == null)
                    throw new IOException();
                Wrapped.Position = mark;
            }
        }

        public virtual long Skip(long cnt)
        {
            if (Wrapped is WrappedSystemStream)
                return ((WrappedSystemStream)Wrapped).InputStream.Skip(cnt);

            long n = cnt;
            while (n > 0)
            {
                if (Read() == -1)
                    return cnt - n;
                n--;
            }
            return cnt - n;
        }

        public bool CanSeek()
        {
            if (Wrapped != null)
                return Wrapped.CanSeek;
            else
                return false;
        }

        public long Position
        {
            get
            {
                if (Wrapped != null)
                    return Wrapped.Position;
                else
                    throw new NotSupportedException();
            }
            set
            {
                if (Wrapped != null)
                    Wrapped.Position = value;
                else
                    throw new NotSupportedException();
            }
        }

        static public InputStream Wrap(Stream s)
        {
            InputStream stream = new InputStream();
            stream.Wrapped = s;
            return stream;
        }
    }

    public class ByteArrayInputStream : InputStream
    {
        public ByteArrayInputStream(byte[] data)
        {
            base.Wrapped = new MemoryStream(data);
        }

        public ByteArrayInputStream(byte[] data, int off, int len)
        {
            base.Wrapped = new MemoryStream(data, off, len);
        }

        public override int Available()
        {
            MemoryStream ms = (MemoryStream)Wrapped;
            return (int)(ms.Length - ms.Position);
        }
    }

    public class WrappedSystemStream : Stream
    {
        private InputStream ist;
        private OutputStream ost;
        int position;
        int markedPosition;

        public WrappedSystemStream(InputStream ist)
        {
            this.ist = ist;
        }

        public WrappedSystemStream(OutputStream ost)
        {
            this.ost = ost;
        }

        public InputStream InputStream
        {
            get { return ist; }
        }

        public OutputStream OutputStream
        {
            get { return ost; }
        }

        public override void Close()
        {
            if (this.ist != null)
            {
                this.ist.Close();
            }
            if (this.ost != null)
            {
                this.ost.Close();
            }
        }

        public override void Flush()
        {
            this.ost.Flush();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            int res = this.ist.Read(buffer, offset, count);
            if (res != -1)
            {
                position += res;
                return res;
            }
            else
                return 0;
        }

        public override int ReadByte()
        {
            int res = this.ist.Read();
            if (res != -1)
                position++;
            return res;
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            if (origin == SeekOrigin.Begin)
                Position = offset;
            else if (origin == SeekOrigin.Current)
                Position = Position + offset;
            else if (origin == SeekOrigin.End)
                Position = Length + offset;
            return Position;
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            this.ost.Write(buffer, offset, count);
            position += count;
        }

        public override void WriteByte(byte value)
        {
            this.ost.Write(value);
            position++;
        }

        public override bool CanRead
        {
            get { return (this.ist != null); }
        }

        public override bool CanSeek
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return (this.ost != null); }
        }

        public override long Length
        {
            get
            {
                throw new NotSupportedException();
            }
        }

        public void OnMark(int nb)
        {
            markedPosition = position;
            ist.Mark(nb);
        }

        public override long Position
        {
            get
            {
                if (ist != null && ist.CanSeek())
                    return ist.Position;
                else
                    return position;
            }
            set
            {
                if (value == position)
                    return;
                else if (value == markedPosition)
                    ist.Reset();
                else if (ist != null && ist.CanSeek())
                {
                    ist.Position = value;
                }
                else
                    throw new NotSupportedException();
            }
        }
    }
    public class OutputStream : IDisposable
    {
        protected Stream Wrapped;

        public static implicit operator OutputStream(Stream s)
        {
            return Wrap(s);
        }

        public static implicit operator Stream(OutputStream s)
        {
            return s.GetWrappedStream();
        }

        public virtual void Close()
        {
            if (this.Wrapped != null)
            {
                this.Wrapped.Close();
            }
        }

        public void Dispose()
        {
            this.Close();
        }

        public virtual void Flush()
        {
            if (this.Wrapped != null)
            {
                this.Wrapped.Flush();
            }
        }

        public Stream GetWrappedStream()
        {
            // Always create a wrapper stream (not directly Wrapped) since the subclass
            // may be overriding methods that need to be called when used through the Stream class
            return new WrappedSystemStream(this);
        }

        static public OutputStream Wrap(Stream s)
        {
            OutputStream stream = new OutputStream();
            stream.Wrapped = s;
            return stream;
        }

        public virtual void Write(int b)
        {
            if (Wrapped is WrappedSystemStream)
                ((WrappedSystemStream)Wrapped).OutputStream.Write(b);
            else
            {
                if (this.Wrapped == null)
                    throw new NotImplementedException();
                this.Wrapped.WriteByte((byte)b);
            }
        }

        public virtual void Write(byte[] b)
        {
            this.Write(b, 0, b.Length);
        }

        public virtual void Write(byte[] b, int offset, int len)
        {
            if (Wrapped is WrappedSystemStream)
                ((WrappedSystemStream)Wrapped).OutputStream.Write(b, offset, len);
            else
            {
                if (this.Wrapped != null)
                {
                    this.Wrapped.Write(b, offset, len);
                }
                else
                {
                    for (int i = 0; i < len; i++)
                    {
                        this.Write(b[i + offset]);
                    }
                }
            }
        }
    }

    public class ObjectInputStream : InputStream
    {
        private BinaryReader reader;

        public ObjectInputStream(InputStream s)
        {
            this.reader = new BinaryReader(s.GetWrappedStream());
        }

        public int ReadInt()
        {
            return this.reader.ReadInt32();
        }
        public long ReadLong()
        {
            return this.reader.ReadInt64();
        }
        public  Boolean ReadBoolean()
        {
            return this.reader.ReadBoolean();
        }

        public object ReadObject()
        {
            throw new NotImplementedException();
        }
    }

    public class ObjectOutputStream : OutputStream
    {
        private BinaryWriter bw;

        public ObjectOutputStream(OutputStream os)
        {
            this.bw = new BinaryWriter(os.GetWrappedStream());
        }

        public virtual void WriteInt(int i)
        {
            this.bw.Write(i);
        }
        public virtual void WriteLong(long i)
        {
            this.bw.Write(i);
        }
        public virtual void WriteBoolean(Boolean i)
        {
            this.bw.Write(i);
        }
        public virtual void WriteObject(object obj)
        {
            
            byte[] bytes = Serialization.SerializeToBytes(obj);
            this.bw.Write(bytes);

        }
    }

    
    public class ByteArrayOutputStream : OutputStream
    {
        public ByteArrayOutputStream()
        {
            base.Wrapped = new MemoryStream();
        }

        public ByteArrayOutputStream(int bufferSize)
        {
            base.Wrapped = new MemoryStream(bufferSize);
        }

        public long Size()
        {
            return ((MemoryStream)base.Wrapped).Length;
        }

        public byte[] ToByteArray()
        {
            return ((MemoryStream)base.Wrapped).ToArray();
        }

        public override void Close()
        {
            // Closing a ByteArrayOutputStream has no effect.
        }

        public override string ToString()
        {
            return System.Text.Encoding.UTF8.GetString(ToByteArray());
        }
    }



}

